package com.tchepannou.limbe.impl;

import com.tchepannou.limbe.Context;
import com.tchepannou.limbe.Interceptor;
import com.tchepannou.limbe.Service;
import com.tchepannou.limbe.Util;
import com.tchepannou.limbe.engine.Engine;
import com.tchepannou.limbe.service.OptionService;
import com.tchepannou.limbe.web.ActionContext;
import com.tchepannou.limbe.web.FilePart;
import com.tchepannou.limbe.web.FilePartImpl;
import com.tchepannou.util.IOUtil;
import com.tchepannou.util.StringUtil;
import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import javax.mail.MessagingException;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.*;

/**
 * Implementation of {@link ActionContext}
 */
public class ActionContextImpl 
    implements ActionContext
{
    //-- Attributes
    private Context _delegate;
    private boolean _rollback;
    private HttpServletRequest _request;
    private HttpServletResponse _response;
    private Map<String, Object> __requestParameters;
    private List<FilePart> __requestFiles;
    
    
    
    //-- Public methods
    public ActionContextImpl (Context context, HttpServletRequest request, HttpServletResponse response)
    {
        _delegate = context;
        _request = request;
        _response = response;
    }

    //-- ActionContext overrides
    @Override
    public HttpServletRequest getRequest ()
    {
        return _request;
    }

    @Override 
    public HttpServletResponse getResponse ()
    {
        return _response;
    }

    @Override 
    public Map<String, Object> getRequestParameters ()
    {
        if (__requestParameters == null)
        {
            load ();
        }
        return __requestParameters;
    }

    @Override 
    public List<FilePart> getRequestFiles ()
    {
        if (__requestFiles == null)
        {
            load ();
        }
        return __requestFiles;
    }

    
    //-- Context overrides
    @Override 
    public String getBasePackage ()
    {
        return _delegate.getBasePackage ();
    }
    
    @Override 
    public List<Interceptor> getInterceptors ()
    {
        return _delegate instanceof Engine
            ? ((Engine)_delegate).getActionInterceptors()
            : null;
    }
        
    @Override 
    public <T extends Service> T findService (Class<T> type)
    {
        return _delegate.findService (type);
    }

    @Override
    public void deliverEmail (String action, Serializable data) throws IOException, MessagingException
    {
        _delegate.deliverEmail(action, data);
    }
    
    /**
     * Call this method to rollback the current transaction
     */
    @Override 
    public void setRollback (boolean rollback)
    {
        _rollback = rollback;
    }

    /**
     * Returns <code>true</code> if the current transaction should be rolled-back
     * @return
     */
    @Override 
    public boolean isRollback ()
    {
        return _rollback;
    }
    
    
    @Override 
    public Util createUtil()
    {
        return _delegate.createUtil ();
    }
    
    @Override
    public ServletContext getServletContext()
    {
        return _delegate.getServletContext();
    }

    //-- Private
    private void load ()
    {
        __requestFiles = new ArrayList<FilePart> ();
        __requestParameters = new HashMap<String, Object> ();
        boolean multipart = ServletFileUpload.isMultipartContent (_request);
        if ( multipart )
        {
            try
            {
                loadMultipart ();
            }
            catch (IOException e)
            {
                throw new IllegalStateException ("IO failure", e);
            }
            catch (FileUploadException e)
            {
                throw new IllegalStateException ("File upload failure", e);
            }
        }
        else
        {
            loadSimple ();
        }
    }

    @SuppressWarnings("rawtypes")
	private void loadSimple ()
    {
        Map map = _request.getParameterMap();
        for (Iterator it = map.entrySet().iterator() ; it.hasNext () ; )
        {
            Map.Entry entry = (Map.Entry)it.next();
            Object value = entry.getValue();
            if (value instanceof String[])
            {
                String[] array = StringUtil.trim((String[])value);
                if (array.length == 1)
                {
                    value = array[0];
                }
            }
            else if (value instanceof String)
            {
                value = StringUtil.trim ((String)value);
            }
            __requestParameters.put ((String)entry.getKey(), value);
        }
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    private void loadMultipart ()
        throws IOException, FileUploadException
    {
        ServletFileUpload upload = new ServletFileUpload ();
        long fileSizeMax = 1024L * 1024L * getFileMaxsize ();
        upload.setFileSizeMax (fileSizeMax);
        FileItemIterator it = upload.getItemIterator(getRequest ());

        /* load parameters and files */
        while (it.hasNext ())
        {
            FileItemStream item = it.next();
            String name = item.getFieldName ();
            byte[] value = getValue(item);
            if (item.isFormField())
            {
                List<String> values = (List<String>)__requestParameters.get (name);
                if (values == null)
                {
                    values = new ArrayList<String> ();
                    __requestParameters.put (name, values);
                }
                values.add (new String(value));
            }
            else
            {
                FilePartImpl file = new FilePartImpl (item.getName(), value);
                __requestFiles.add (file);
            }
        }

        /* convert values to string arrays */
        for (Object name : __requestParameters.keySet ())
        {
            List<String> values = (List<String>)__requestParameters.get (name);
            String[] xvalues = (String[])values.toArray (new String[] {});
            if (xvalues.length == 1)
            {
                __requestParameters.put (name.toString(), xvalues[0]);
            }
            else
            {
                __requestParameters.put (name.toString (), xvalues);
            }
        }
    }

    private long getFileMaxsize()
    {
        OptionService os = _delegate.findService (OptionService.class);
        String value = os.get (OptionService.OPTION_ASSET_UPLOAD_MAX_SIZE, null);
        return StringUtil.toLong (value, OptionService.DEFAULT_ASSET_UPLOAD_MAX_SIZE);
    }
    
    private byte[] getValue (FileItemStream item)
        throws IOException
    {
        ByteArrayOutputStream out = new ByteArrayOutputStream ();
        InputStream in = item.openStream ();
        try
        {
            IOUtil.copy (in, out);
            return out.toByteArray();
        }
        finally
        {
            
        }
    }


}
